#pragma once
#include <iostream>
#include <cassert>
#include <mutex>
#include <set>
#include <unordered_map>

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif

typedef size_t PageID;
static const size_t list_num = 208; // 
static const size_t max_byte = 256 * 1024;
static const size_t page_num = 129; // 最大页数128
static const size_t page_shift = 13; // 2^13 = 8KB, 每页内存大小


// size                        对齐数         范围
//[1, 128]                    8            [0, 16)
//[128+1, 1024]               16           [16, 72)
//[1024+1, 8*1024]            128          [72, 128)
//[8*1024, 64* 1024]          1024         [128, 184)
//[64*1024, 256*1024]         8*1024       [184, 208)

// 工具类
class Tool
{
public:
    // 取前4/8字节存下一内存块地址
    static void*& NextObj(void* obj);
    // 对齐内存
    static size_t RoundUp(size_t size);
    // 在哈希表中位置
    static size_t Index(size_t size);
    // size大小内存块能申请的最大数量
    static size_t NumMoveSize(size_t size);
    // size大小内存块最大申请页数
    static size_t NumMovePage(size_t size);

private:
    static size_t _RoundUp(size_t size, size_t alignNum);

    static const size_t group_num[5];
};


// 自由链表，挂载内存块
class FreeList
{
public:
    FreeList();

    size_t GetSize();
    size_t& GetMaxSize(); // 引用，直接用这个接口修改maxSize
    bool Empty();
    void Push(void* obj);
    void* Pop();
    void PushRange(void* start, void* end, size_t n);
    void PopRange(void*& start, void*& end, size_t size); // 输出型形参

private:
    void* _freeList; // 结点，指向下一个内存块
    size_t _size;     // 链表有几个内存块
    size_t _maxSize;  // 链表最多申请几个内存块,
};

// cc中管理FreeList, pc中直接管理内存
struct Span
{
    PageID _id = 0;       // 页号
    size_t _nPage = 0;       // 页数
    size_t _objSize = 0;       // 内存块大小
    size_t _useCount = 0;       // 内存块使用情况
    void* _freeList = nullptr; // 挂载内存块的起始地址
    Span* _next = nullptr; // 下一span
    Span* _prev = nullptr; // 上一span
};

// 双向带头循环链表
class SpanList
{
public:
    SpanList();

    bool Empty();
    Span* Begin();
    Span* End();

    void Insert(Span* pos, Span* span);
    void Erase(Span* pos);
    void PushFront(Span* span);
    Span* PopFront();
    std::mutex& GetMtx();

private:
    Span* _head;
    std::mutex _mtx;
};


inline static void* SystemAlloc(size_t npage) {
#ifdef _WIN32
    void* ptr = VirtualAlloc(0, npage << page_shift, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#elif __linux__
    void* ptr = sbrk(npage * (1 <<page_shift));
#endif
    if (ptr == nullptr) {
        throw std::bad_alloc();
    }
    return ptr;
}


inline static void SystemFree(void* ptr) {
#ifdef _WIN32
    VirtualFree(ptr, 0, MEM_RELEASE);
#elif __linux__
    brk(ptr);
#endif
}